home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / DELPHI / GIFCODE.ZIP / GIFCODE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-04-11  |  24.4 KB  |  710 lines

  1. unit GifCode;
  2. {Freeware GIF image component}
  3.  
  4. {Based on GifUtl.pas (c)1993 Sean Wenzel     Compuserve 71736,1245}
  5.  
  6. {Converted to Delphi by Richard Dominelli     RichardA_Dominelli@mskcc.org
  7.                                                             dopey@felix.mskcc.org
  8.                                                              Compuserve 73541,2555}
  9. {Converted to Delphi 2 and made into an
  10. image component by Richard Shotbolt            Compuserve 100327,2305
  11. }
  12.  
  13. interface
  14.  
  15. uses WinTypes,
  16.   Forms,
  17.   SysUtils,
  18.   Classes,
  19.   ExtCtrls;
  20.  
  21. const
  22.     { image descriptor bit masks }
  23.     idLocalColorTable = $80;    { set if a local color table follows }
  24.     idInterlaced = $40;            { set if image is interlaced }
  25.     idSort = $20;                    { set if color table is sorted }
  26.     idReserved = $0C;                { reserved - must be set to $00 }
  27.     idColorTableSize = $07;        { size of color table as above }
  28.     Trailer: byte = $3B;            { indicates the end of the GIF data stream }
  29.   ExtensionIntroducer: byte = $21;
  30.     MAXSCREENWIDTH = 800;
  31.   ImageSeparator: byte = $2C;
  32. { logical screen descriptor packed field masks }
  33.     lsdGlobalColorTable = $80;    { set if global color table follows L.S.D. }
  34.     lsdColorResolution = $70;    { Color resolution - 3 bits }
  35.     lsdSort = $08;                    { set if global color table is sorted - 1 bit }
  36.     lsdColorTableSize = $07;    { size of global color table - 3 bits }
  37.                                         { Actual size = 2^value+1    - value is 3 bits }
  38.     BlockTerminator: byte = 0; { terminates stream of data blocks }
  39.     MAXCODES = 4095;                { the maximum number of different codes 0 inclusive }
  40. { error constants }
  41.     geNoError = 0;       { no errors found }
  42.     geNoFile = 1;        { gif file not found }
  43.     geNotGIF = 2;        { file is not a gif file }
  44.     geNoGlobalColor = 3; { no Global Color table found }
  45.     geImagePreceded = 4; { image descriptor preceeded by other unknown data }
  46.     geEmptyBlock = 5;     { Block has no data }
  47.      geUnExpectedEOF = 6; { unexpected EOF }
  48.     geBadCodeSize = 7;   { bad code size }
  49.     geBadCode = 8;           { Bad code was found }
  50.     geBitSizeOverflow = 9; { bit size went beyond 12 bits }
  51.   geNoBMP = 10;             { Could not make BMP file }
  52.  
  53. ErrName: Array[1..10] of string = (
  54.     'GIF file not found',
  55.     'Not a GIF file',
  56.     'Missing color table',
  57.     'Bad data',
  58.     'No data',
  59.      'Unexpected EOF',
  60.     'Bad code size',
  61.     'Bad code',
  62.     'Bad bit size',
  63.   'Bad bitmap');
  64.  
  65. CodeMask: array[0..12] of integer = (  { bit masks for use with Next code }
  66.     0,
  67.     $0001, $0003,
  68.     $0007, $000F,
  69.     $001F, $003F,
  70.     $007F, $00FF,
  71.     $01FF, $03FF,
  72.     $07FF, $0FFF);
  73.  
  74. type
  75.     TDataSubBlock = record
  76.         Size: byte;     { size of the block -- 0 to 255 }
  77.         Data: array[1..255] of byte; { the data }
  78.     end;
  79.  
  80. type
  81.     THeader = record
  82.         Signature: array[0..2] of char; { contains 'GIF' }
  83.         Version: array[0..2] of char;   { '87a' or '89a' }
  84.     end;
  85.  
  86. TLogicalScreenDescriptor = record
  87.     ScreenWidth: word;              { logical screen width }
  88.     ScreenHeight: word;  { logical screen height }
  89.     PackedFields: byte;     { packed fields - see below }
  90.     BackGroundColorIndex: byte;     { index to global color table }
  91.     AspectRatio: byte;      { actual ratio = (AspectRatio + 15) / 64 }
  92. end;
  93.  
  94. type
  95.     TColorItem = record            { one item a a color table }
  96.         Red: byte;
  97.         Green: byte;
  98.         Blue: byte;
  99.     end;
  100.  
  101. TColorTable = array[0..255] of TColorItem;    { the color table }
  102.  
  103. type
  104.     TImageDescriptor = record
  105.         Separator: byte;        { fixed value of ImageSeparator }
  106.         ImageLeftPos: word;     { Column in pixels in respect to left edge of logical screen }
  107.         ImageTopPos: word;    { row in pixels in respect to top of logical screen }
  108.         ImageWidth: word;        { width of image in pixels }
  109.         ImageHeight: word;     { height of image in pixels }
  110.         PackedFields: byte;    { see below }
  111.     end;
  112.  
  113. { other extension blocks not currently supported by this unit
  114.     - Graphic Control extension
  115.     - Comment extension           I'm not sure what will happen if these blocks
  116.     - Plain text extension        are encountered but it'll be interesting
  117.     - application extension }
  118.  
  119. type
  120.     TExtensionBlock = record
  121.         Introducer: byte;                               { fixed value of ExtensionIntroducer }
  122.         ExtensionLabel: byte;
  123.         BlockSize: byte;
  124.   end;
  125.  
  126.     PCodeItem = ^TCodeItem;
  127.  
  128.     TCodeItem = record
  129.         Code1, Code2: byte;
  130.   end;
  131. {===============================================================}
  132. {    Bitmap File Structs                                                                  
  133. {===============================================================}
  134.  
  135. type
  136.     GraphicLine = array [0..2048] of byte;
  137.     PBmLine = ^TBmpLinesStruct;
  138.     TBmpLinesStruct = record
  139.         LineData : GraphicLine;
  140.         LineNo : Integer;
  141.         end;
  142. {------------------------------------------------------------------------------}
  143.  
  144. type
  145.     { This is the actual gif object }
  146.     PGif = ^TGif;
  147.     TGif = class(TObject)
  148.   private
  149.         GifStream: TMemoryStream;    { the file stream for the gif file }
  150.         Header: THeader;          { gif file header }
  151.         LogicalScreen: TLogicalScreenDescriptor;  { gif screen descriptor }
  152.         GlobalColorTable: TColorTable;        { global color table }
  153.         LocalColorTable: TColorTable;            { local color table }
  154.         ImageDescriptor: TImageDescriptor;     { image descriptor }
  155.         UseLocalColors: boolean;        { true if local colors in use }
  156.         Interlaced: boolean;                { true if image is interlaced }
  157.         LZWCodeSize: byte;                { minimum size of the LZW codes in bits }
  158.         ImageData: TDataSubBlock;        { variable to store incoming gif data }
  159.         TableSize: word;                    { number of entrys in the color table }
  160.         BitsLeft, BytesLeft: integer;    { bits left in byte - bytes left in block }
  161.         BadCodeCount: word;              { bad code counter }
  162.         CurrCodeSize: integer;           { Current size of code in bits }
  163.         ClearCode: integer;              { Clear code value }
  164.         EndingCode: integer;             { ending code value }
  165.         Slot: word;                            { position that the next new code is to be added }
  166.         TopSlot: word;            { highest slot position for the current code size }
  167.         HighCode: word;        { highest code that does not require decoding }
  168.         NextByte: integer;    { the index to the next byte in the datablock array }
  169.         CurrByte: byte;          { the current byte }
  170.         DecodeStack: array[0..MAXCODES] of byte; { stack for the decoded codes }
  171.         Prefix: array[0..MAXCODES] of integer;                     { array for code prefixes }
  172.         Suffix: array[0..MAXCODES] of integer;             { array for code suffixes }
  173.         LineBuffer: GraphicLine; { array for buffer line output }
  174.         CurrentX, CurrentY: integer;                                            { current screen locations }
  175.         Status: word;             
  176.         InterlacePass: byte;    { interlace pass number }
  177.         {Conversion Routine Vars}
  178.         BmHeader : TBitmapInfoHeader; {File Header for bitmap file}
  179.         ImageLines: TList; {Image data}
  180.      BmpStream: TMemoryStream;
  181.         {Member Functions}
  182.      procedure ParseMem;
  183.         function NextCode: word;     { returns the next available code }
  184.      procedure Error(ErrCode: integer);
  185.         procedure InitCompressionStream;   { initializes info for decode }
  186.         procedure ReadSubBlock;  { reads a data subblock from the stream }
  187.         procedure CreateLine;
  188.      procedure CreateBitHeader; {Takes the gif header information and converts it to BMP}
  189.   public
  190.         constructor Create;
  191.         destructor Destroy; override;
  192.         procedure Decode;
  193.         procedure GifToBmp(AGifName, ABmpName: string);
  194.      procedure GifConvert(AGifName: string);
  195.         procedure ConvertfromMem(AMemStream:TMemoryStream;ABmpName:string);
  196.      procedure WriteBitmapToStream;
  197.         procedure WriteBitmapToFile(ABMPName: string); {Writes out the header info
  198.                                            writes out the pallet in correct order.
  199.                                            Arranges the lines in correct order.
  200.                                            Writes out the image lines in correct order}
  201.  
  202.     end;
  203.  
  204. type
  205.     TGifImage = class(TImage)
  206.   private
  207.       FGifFileName: string;
  208.       IGif: TGif;
  209.      procedure SetGifFileName(Value: string);
  210.   public
  211.         constructor Create(AOwner:TComponent); override;
  212.         destructor Destroy; override;
  213.   published
  214.       property GifFileName: string read FGifFileName write SetGifFileName;
  215. end;
  216.  
  217. type
  218.     EGifException = class(Exception)
  219. end;
  220.  
  221. procedure Register;
  222.  
  223. implementation
  224.  
  225. function Power(A, N: real): real; { returns A raised to the power of N }
  226. begin
  227. Power := exp(N * ln(A));
  228. end;
  229.  
  230. {------------------------------------------------------------------------------}
  231. { TGifImage }
  232.  
  233. constructor TGifImage.Create(AOwner:TComponent);
  234. begin
  235. IGif := TGif.Create;
  236. inherited Create(AOwner);
  237. end;
  238.  
  239. destructor TGifImage.Destroy;
  240. begin
  241. IGif.Free;
  242. inherited Destroy;
  243. end;
  244.  
  245. procedure TGifImage.SetGifFileName(Value: string);
  246. {Loads the GIF file into the image}
  247. {If you don't like the delay turn the hourglass on}
  248. begin
  249.   try
  250.     Picture.Bitmap := nil;     { Clear the image }
  251.     IGif.GifConvert(Value);{ Convert GIF file to in-memory bitmap }
  252.     Picture.Bitmap.LoadFromStream(IGif.BmpStream); { Load new BMP from memory }
  253.     if Visible then                    { Force a repaint (avoids flecks etc }
  254.        Paint;
  255.     FGifFileName := UpperCase(Value);
  256.   except
  257.     on Exception do
  258.        begin
  259.        Picture.Bitmap := nil;    { No picture }
  260.        IGif.Free;                      { Free then recreate TGif }
  261.        IGif := TGif.Create;
  262.        Beep;
  263.        FGifFileName := '';
  264.        end;
  265.   end;
  266. end;
  267.  
  268. {------------------------------------------------------------------------------}
  269.  
  270. { TGif }
  271. constructor TGif.Create;
  272. begin
  273. {Create Memory Buffer to hold gif}
  274. GifStream := TMemoryStream.Create;
  275. BmpStream := TMemoryStream.Create;
  276. ImageLines := TList.Create;
  277. end;
  278. {------------------------------------------------------------------------------}
  279.  
  280. destructor TGif.Destroy;
  281. begin
  282. GifStream.Free;
  283. BmpStream.Free;
  284. ImageLines.Free;
  285. inherited Destroy;
  286. end;
  287. {------------------------------------------------------------------------------}
  288.  
  289. procedure TGif.GifToBmp(AGifName, ABmpName: string);
  290. {Convert GIF file to BMP file}
  291. begin
  292. GifConvert(AGifName);
  293. BmpStream.SaveToFile(ABMPName);
  294. end;
  295. {------------------------------------------------------------------------------}
  296.  
  297. procedure TGif.GifConvert(AGifName: string);
  298. begin
  299. { Converts GIF file to bitstream }
  300. GifStream.LoadFromFile(AGifName); { Load the file into memory }
  301. ParseMem;
  302. { Create the bitmap header info }
  303. CreateBitHeader;
  304. { Decode the GIF }
  305. Decode;
  306. WriteBitmapToStream;
  307. end;
  308. {------------------------------------------------------------------------------}
  309.  
  310. procedure TGif.ConvertfromMem(AMemStream: TMemoryStream; ABmpName: string);
  311. begin
  312. GifStream.LoadFromStream(AMemStream);
  313. GifConvert(ABmpName);
  314. end;
  315. {------------------------------------------------------------------------------}
  316.  
  317. {Raise exception with a message}
  318. procedure TGif.Error(ErrCode: integer);
  319. begin
  320. Raise EGifException.Create(ErrName[ErrCode]);
  321. end;
  322. {------------------------------------------------------------------------------}
  323.  
  324. procedure TGif.ParseMem;
  325. {Decodes the header and palette info}
  326. begin
  327. GifStream.Read(Header, sizeof(Header)); { read the header }
  328. {Stupid validation tricks}
  329. if Header.Signature <> 'GIF' then
  330.     Error(geNotGif);  { is vaild signature }
  331.     {Decode the header information}
  332. GifStream.Read(LogicalScreen, sizeof(LogicalScreen));
  333. if LogicalScreen.PackedFields and lsdGlobalColorTable = lsdGlobalColorTable then
  334.     begin
  335.     TableSize := Trunc(Power(2,(LogicalScreen.PackedFields and lsdColorTableSize)+1));
  336.     GifStream.Read(GlobalColorTable, TableSize*sizeof(TColorItem)); { read Global Color Table }
  337.     end
  338. else
  339.     Error(geNoGlobalColor);
  340. {Done with Global Headers}
  341. {Image specific headers}
  342. GifStream.Read(ImageDescriptor, sizeof(ImageDescriptor)); { read image descriptor }
  343. {Decode image header info}
  344. if ImageDescriptor.Separator <> ImageSeparator then                     { verify that it is the descriptor }
  345.     Error(geImagePreceded);
  346. {Check for local color table}
  347. if ImageDescriptor.PackedFields and idLocalColorTable = idLocalColorTable then
  348.     begin                                                               { if local color table }
  349.     TableSize := Trunc(Power(2,(ImageDescriptor.PackedFields and idColorTableSize)+1));
  350.     GifStream.Read(LocalColorTable, TableSize*sizeof(TColorItem)); { read Local Color Table }
  351.     UseLocalColors := True;
  352.     end
  353. else
  354.     UseLocalColors := False;
  355.     {Check for interlaced}
  356. if ImageDescriptor.PackedFields and idInterlaced = idInterlaced then
  357.     begin
  358.     Interlaced := true;
  359.     InterlacePass := 0;
  360.     end;
  361. {End of image header stuff}
  362. {Reset then Expand capacity of the Image Lines list}
  363. ImageLines.Clear;
  364. ImageLines.Capacity := ImageDescriptor.ImageHeight;
  365. if (GifStream = nil) then    { check for stream error }
  366.     Error(geNoFile);
  367. end;
  368. {------------------------------------------------------------------------------}
  369.  
  370. procedure TGif.InitCompressionStream;
  371. begin
  372. {InitGraphics;}                                   { Initialize the graphics display }
  373. GifStream.Read(LZWCodeSize, sizeof(byte));    { get minimum code size }
  374. if not (LZWCodeSize in [2..9]) then         { valid code sizes 2-9 bits }
  375.    Error(geBadCodeSize);
  376. CurrCodeSize := succ(LZWCodeSize); { set the initial code size }
  377. ClearCode := 1 shl LZWCodeSize;    { set the clear code }
  378. EndingCode := succ(ClearCode);     { set the ending code }
  379. HighCode := pred(ClearCode);           { set the highest code not needing decoding }
  380. BytesLeft := 0;                    { clear other variables }
  381. BitsLeft := 0;
  382. CurrentX := 0;
  383. CurrentY := 0;
  384. end;
  385. {------------------------------------------------------------------------------}
  386.  
  387. procedure TGif.ReadSubBlock;
  388. begin
  389. GifStream.Read(ImageData.Size, sizeof(ImageData.Size)); { get the data block size }
  390. if ImageData.Size = 0 then
  391.     Error(geEmptyBlock);                                    { check for empty block }
  392. GifStream.Read(ImageData.Data, ImageData.Size);     { read in the block }
  393. NextByte := 1;                                 { reset next byte }
  394. BytesLeft := ImageData.Size;                        { reset bytes left }
  395. end;
  396. {------------------------------------------------------------------------------}
  397.  
  398. function TGif.NextCode: word; { returns a code of the proper bit size }
  399. begin
  400. if BitsLeft = 0 then        { any bits left in byte ? }
  401.     begin                         { any bytes left }
  402.     if BytesLeft <= 0 then     { if not get another block }
  403.         ReadSubBlock;
  404.   CurrByte := ImageData.Data[NextByte];     { get a byte }
  405.   inc(NextByte);                            { set the next byte index }
  406.   BitsLeft := 8;                            { set bits left in the byte }
  407.   dec(BytesLeft);                           { decrement the bytes left counter }
  408.   end;
  409. Result := CurrByte shr (8 - BitsLeft);            { shift off any previosly used bits}
  410.     while CurrCodeSize > BitsLeft do            { need more bits ? }
  411.         begin
  412.         if BytesLeft <= 0 then                        { any bytes left in block ? }
  413.             ReadSubBlock;                       { if not read in another block }
  414.         CurrByte := ImageData.Data[NextByte];     { get another byte }
  415.         inc(NextByte);                            { increment NextByte counter }
  416.         Result := Result or (CurrByte shl BitsLeft);    { add the remaining bits to the return value }
  417.         BitsLeft := BitsLeft + 8;              { set bit counter }
  418.         Dec(BytesLeft);                         { decrement bytesleft counter }
  419.     end;
  420. BitsLeft := BitsLeft - CurrCodeSize;  { subtract the code size from bitsleft }
  421. Result := Result and CodeMask[CurrCodeSize];{ mask off the right number of bits }
  422. end;
  423.  
  424. {------------------------------------------------------------------------------}
  425.  
  426. procedure TGif.Decode;
  427. { this procedure actually decodes the GIF image }
  428. var
  429.     SP: integer; { index to the decode stack }
  430.  
  431. { local procedure that decodes a code and puts it on the decode stack }
  432. procedure DecodeCode(var Code: word);
  433. begin
  434.     while Code > HighCode do { rip thru the prefix list placing suffixes }
  435.     begin                    { onto the decode stack }
  436.         DecodeStack[SP] := Suffix[Code]; { put the suffix on the decode stack }
  437.         inc(SP);                         { increment decode stack index }
  438.         Code := Prefix[Code];            { get the new prefix }
  439.     end;
  440.     DecodeStack[SP] := Code;                { put the last code onto the decode stack }
  441.     inc(SP);                                { increment the decode stack index }
  442. end;
  443.  
  444. var
  445.     TempOldCode, OldCode: word;
  446.     BufCnt: word;    { line buffer counter }
  447.     Code, C: word;
  448.     CurrBuf: word;    { line buffer index }
  449.   MaxVal: boolean;
  450. begin
  451. InitCompressionStream;    { Initialize decoding paramaters }
  452. OldCode := 0;
  453. SP := 0;
  454. BufCnt := ImageDescriptor.ImageWidth; { set the Image Width }
  455. CurrBuf := 0;
  456. MaxVal := False;
  457. C := NextCode;                { get the initial code - should be a clear code }
  458. while C <> EndingCode do  { main loop until ending code is found }
  459.     begin
  460.     if C = ClearCode then    { code is a clear code - so clear }
  461.         begin
  462.         CurrCodeSize := LZWCodeSize + 1;    { reset the code size }
  463.         Slot := EndingCode + 1;                { set slot for next new code }
  464.         TopSlot := 1 shl CurrCodeSize;    { set max slot number }
  465.         while C = ClearCode do
  466.             C := NextCode;    { read until all clear codes gone - shouldn't happen }
  467.         if C = EndingCode then
  468.             Error(geBadCode);       { ending code after a clear code }
  469.         if C >= Slot then { if the code is beyond preset codes then set to zero }
  470.             C := 0;
  471.         OldCode := C;
  472.         DecodeStack[sp] := C;     { output code to decoded stack }
  473.         inc(SP);                    { increment decode stack index }
  474.         end
  475.     else   { the code is not a clear code or an ending code so it must }
  476.         begin  { be a code code - so decode the code }
  477.         Code := C;
  478.         if Code < Slot then     { is the code in the table? }
  479.             begin
  480.             DecodeCode(Code);                 { decode the code }
  481.             if Slot <= TopSlot then
  482.                 begin                           { add the new code to the table }
  483.                 Suffix[Slot] := Code;          { make the suffix }
  484.                 PreFix[slot] := OldCode;     { the previous code - a link to the data }
  485.                 inc(Slot);         { increment slot number }
  486.                 OldCode := C;        { set oldcode }
  487.                 end;
  488.             if Slot >= TopSlot then         { have reached the top slot for bit size }
  489.                 begin                       { increment code bit size }
  490.                 if CurrCodeSize < 12 then     { new bit size not too big? }
  491.                     begin
  492.                     TopSlot := TopSlot shl 1;    { new top slot }
  493.                     inc(CurrCodeSize)             { new code size }
  494.                     end
  495.            else
  496.                MaxVal := True;             { Must check next code is a start code }
  497.                 end;
  498.             end
  499.         else
  500.             begin    { the code is not in the table }
  501.         if Code <> Slot then
  502.                 Error(geBadCode); { so error out }
  503.             { the code does not exist so make a new entry in the code table
  504.             and then translate the new code }
  505.             TempOldCode := OldCode;  { make a copy of the old code }
  506.             while OldCode > HighCode do     { translate the old code and place it }
  507.                 begin                          { on the decode stack }
  508.                 DecodeStack[SP] := Suffix[OldCode]; { do the suffix }
  509.                 OldCode := Prefix[OldCode];         { get next prefix }
  510.                 end;
  511.             DecodeStack[SP] := OldCode;    { put the code onto the decode stack }
  512.                                                 { but DO NOT increment stack index }
  513.             { the decode stack is not incremented because because we are only
  514.             translating the oldcode to get the first character }
  515.             if Slot <= TopSlot then
  516.                 begin     { make new code entry }
  517.                 Suffix[Slot] := OldCode;         { first char of old code }
  518.                 Prefix[Slot] := TempOldCode;     { link to the old code prefix }
  519.                 inc(Slot);                       { increment slot }
  520.                 end;
  521.             if Slot >= TopSlot then { slot is too big }
  522.                 begin                        { increment code size }
  523.                 if CurrCodeSize < 12 then
  524.                     begin
  525.                     TopSlot := TopSlot shl 1;    { new top slot }
  526.               inc(CurrCodeSize);           { new code size }
  527.                     end
  528.            else
  529.                MaxVal := True;             { Must check next code is a start code }
  530.                 end;
  531.             DecodeCode(Code); { now that the table entry exists decode it }
  532.             OldCode := C;     { set the new old code }
  533.             end;
  534.         end;
  535.         { the decoded string is on the decode stack so pop it off and put it
  536.          into the line buffer }
  537.         while SP > 0 do
  538.             begin
  539.             dec(SP);
  540.             LineBuffer[CurrBuf] := DecodeStack[SP];
  541.             inc(CurrBuf);
  542.             dec(BufCnt);
  543.             if BufCnt = 0 then  { is the line full ? }
  544.                 begin
  545.                 CreateLine;
  546.                 CurrBuf := 0;
  547.                 BufCnt := ImageDescriptor.ImageWidth;
  548.                 end;
  549.             end;
  550.         C := NextCode;    { get the next code and go at is some more }
  551.      if (MaxVal = True) and (C <> ClearCode) then
  552.          Error(geBitSizeOverflow);
  553.      MaxVal := False;
  554.     end;
  555. end;
  556. {------------------------------------------------------------------------------}
  557.  
  558. procedure TGif.CreateBitHeader;
  559. { This routine takes the values from the GIF image
  560.     descriptor and fills in the appropriate values in the
  561.     bit map header struct. }
  562. begin
  563. BmHeader.biSize := Sizeof(TBitmapInfoHeader);
  564. BmHeader.biWidth := ImageDescriptor.ImageWidth;
  565. BmHeader.biHeight := ImageDescriptor.ImageHeight;
  566. BmHeader.biPlanes := 1; {Arcane and rarely used}
  567. BmHeader.biBitCount := 8; {Hmmm Should this be hardcoded ?}
  568. BmHeader.biCompression := BI_RGB; {Sorry Did not implement compression in this version}
  569. BmHeader.biSizeImage := 0; {Valid since we are not compressing the image}
  570. BmHeader.biXPelsPerMeter :=143; {Rarely used very arcane field}
  571. BmHeader.biYPelsPerMeter :=143; {Ditto}
  572. BmHeader.biClrUsed := 0; {all colors are used}
  573. BmHeader.biClrImportant := 0; {all colors are important}
  574. end;
  575. {------------------------------------------------------------------------------}
  576.  
  577. {fills in Line list with current line}
  578. procedure TGif.CreateLine;
  579. var
  580.   p: PBmLine;
  581. begin
  582. Application.ProcessMessages;
  583. {Create a new bmp line}
  584. New(p);
  585. {Fill in the data}
  586. p^.LineData := LineBuffer;
  587. p^.LineNo := CurrentY;
  588. {Add it to the list of lines}
  589. ImageLines.Add(p);
  590. {Prepare for the next line}
  591. Inc(CurrentY);
  592. if InterLaced then
  593.     { Interlace support }
  594.     begin
  595.     case InterlacePass of
  596.         0: CurrentY := CurrentY + 7;
  597.         1: CurrentY := CurrentY + 7;
  598.         2: CurrentY := CurrentY + 3;
  599.         3: CurrentY := CurrentY + 1;
  600.         end;
  601.     if CurrentY >= ImageDescriptor.ImageHeight then
  602.     begin
  603.         Inc(InterLacePass);
  604.         case InterLacePass of
  605.             1: CurrentY := 4;
  606.             2: CurrentY := 2;
  607.             3: CurrentY := 1;
  608.             end;
  609.         end;
  610.     end;
  611. end;
  612. {------------------------------------------------------------------------------}
  613.  
  614. procedure TGif.WriteBitmapToStream;
  615. var
  616.   BitFile: TBitmapFileHeader;
  617.   i: integer;
  618.   Line: integer;
  619.   ch: char;
  620.   p: PBmLine;
  621.   x: integer;
  622. begin
  623. BitFile.bfSize := (3*255) + {Color map info}
  624.     sizeof(TBitmapFileHeader) +
  625.     sizeof(TBitmapInfoHeader) +
  626.     (ImageDescriptor.ImageHeight*ImageDescriptor.ImageWidth);
  627. BitFile.bfReserved1 := 0; {not currently used}
  628. BitFile.bfReserved2 := 0; {not currently used}
  629. BitFile.bfOffBits := (4*256)+
  630.     sizeof(TBitmapFileHeader)+
  631.     sizeof(TBitmapInfoHeader);
  632. {Write the file header}
  633. BmpStream.Clear;
  634. ch:='B';
  635. BmpStream.Write(ch,1);
  636. ch:='M';
  637. BmpStream.Write(ch,1);
  638. BmpStream.Write(BitFile.bfSize,sizeof(BitFile.bfSize));
  639. BmpStream.Write(BitFile.bfReserved1,sizeof(BitFile.bfReserved1));
  640. BmpStream.Write(BitFile.bfReserved2,sizeof(BitFile.bfReserved2));
  641. BmpStream.Write(BitFile.bfOffBits,sizeof(BitFile.bfOffBits));
  642. {Write the bitmap image header info}
  643. BmpStream.Write(BmHeader,sizeof(BmHeader));
  644. {Write the BGR palete inforamtion to this file}
  645. if UseLocalColors then {Use the local color table}
  646.     begin
  647.     for i:= 0 to 255 do
  648.         begin
  649.         BmpStream.Write(LocalColorTable[i].Blue,1);
  650.         BmpStream.Write(LocalColorTable[i].Green,1);
  651.         BmpStream.Write(LocalColorTable[i].Red,1);
  652.         BmpStream.Write(ch,1); {Bogus palete entry required by windows}
  653.      end;
  654.     end
  655. else {Use the global table}
  656.     begin
  657.     for i:= 0 to 255 do
  658.         begin
  659.         BmpStream.Write(GlobalColorTable[i].Blue,1);
  660.         BmpStream.Write(GlobalColorTable[i].Green,1);
  661.         BmpStream.Write(GlobalColorTable[i].Red,1);
  662.         BmpStream.Write(ch,1); {Bogus palete entry required by windows}
  663.      end;
  664.     end;
  665. {Init the Line Counter}
  666. Line := ImageDescriptor.ImageHeight;
  667. {Write out File lines in reverse order}
  668. while Line >= 0 do
  669.     begin
  670.     {Go through the line list in reverse order looking for the
  671.     current Line. Use reverse order since non interlaced gifs are
  672.     stored top to bottom.  Bmp file need to be written bottom to
  673.     top}
  674.     for i := (ImageLines.Count - 1) downto 0  do
  675.         begin
  676.         p := ImageLines.Items[i];
  677.         if p^.LineNo = Line then
  678.             begin
  679.         x := ImageDescriptor.ImageWidth;
  680.             BmpStream.Write(p^.LineData, x);
  681.         ch := chr(0);
  682.         while (x and 3) <> 0 do { Pad up to 4-byte boundary with zeroes }
  683.             begin
  684.            Inc(x);
  685.            BmpStream.Write(ch, 1);
  686.            end;
  687.             break;
  688.             end;
  689.         end;
  690.     Dec(Line);
  691.     end;
  692. BmpStream.Seek(0,0); { reset mewmory stream}
  693. end;
  694.  
  695. {------------------------------------------------------------------------------}
  696.  
  697. procedure TGif.WriteBitmapToFile(ABMPName: string);
  698. begin
  699. WriteBitMapToStream;
  700. BmpStream.SaveToFile(ABMPName);
  701. end;
  702.  
  703. {------------------------------------------------------------------------------}
  704.  
  705. procedure Register;
  706. begin
  707. RegisterComponents('Samples',[TGifImage]);
  708. end;
  709. end.
  710.